#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#include "progress_bar.h"

static int judge_initial_value(struct progress_bar *progress_data);
static void instead_of_enter(void);
static void draw_outside(int progress_outside_color, int length, int x_direction, int y_direction);
static void draw_inside(int color, int length, int speed, int x_direction, int y_direction);

/*
 *@brief  从terminal得到进度条的参数，参数错误则直接结束程序
 * */
void get_terminal_param(int param_argc, char *param_argv[], struct progress_bar *progress_param)
{
    if (!progress_param) {
        printf("PARAM ERROR!\n");
        system("stty echo");
        system("clear");
        exit(0);
    }

    int opt, ret;

    while ((opt = getopt(param_argc, param_argv, "l:s:o:i:x:y:")) != -1) {
        switch (opt) {
            case 'l':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("ERROR: Input wrong length!\n");
                    system("stty echo");
                    exit(0);
                }
                else
                   progress_param->length = atoi(optarg);
                break;

            case 's':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("ERROR: Input wrong speed!\n");
                    system("stty echo");
                    exit(0);
                }
                else
                   progress_param->speed = atoi(optarg) * 1000; 
                break;

            case 'o':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("ERROR: Input wrong color!\n");
                    system("stty echo");
                    exit(0);
                }
                else
                   progress_param->outside_color = atoi(optarg); 
                break;

            case 'i':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("ERROR: Input wrong color!\n");
                    system("stty echo");
                    exit(0);
                }
                else
                   progress_param->inside_color = atoi(optarg); 
                break;

            case 'x':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("ERROR: Input wrong color!\n");
                    system("stty echo");
                    exit(0);
                }
                else
                   progress_param->x_direction = atoi(optarg); 
                break;

            case 'y':
                if (atoi(optarg) < 0 || atoi(optarg) > 256) {
                    printf("ERROR: Input wrong color!\n");
                    system("stty echo");
                    exit(0);
                }
                else
                   progress_param->y_direction = atoi(optarg); 
                break;

            default:
                break;
        }
    }

    ret = judge_initial_value(progress_param);

    if (ret == 0) {
        system("clear");
        system("stty echo");
        exit(0);
    }
}

/*
 *@brief  初始化,设置默认参数
 * */
void progress_bar_init(struct progress_bar *progress_data)
{
    if (!progress_data) {
        printf("PARAM ERROR!\n");
        system("stty echo");
        system("clear");
        exit(0);
    }

    progress_data->speed = 10000;
    progress_data->outside_color = 42;
    progress_data->inside_color = 45;
    progress_data->length = 100;
    progress_data->x_direction = 0;
    progress_data->y_direction = 0;
}

/*
 *@brief  运行进度条
 * */
void progress_bar_run(struct progress_bar progress_data)
{
    system("clear");

    draw_outside(progress_data.outside_color,
                 progress_data.length+2,
                 progress_data.x_direction,
                 progress_data.y_direction);

    draw_inside(progress_data.inside_color,
                         progress_data.length, 
                         progress_data.speed, 
                         progress_data.x_direction, 
                         progress_data.y_direction);
}

/*
 *@brief  判断初始值是否正确
 *@return 参数错误返回0，正确返回1
 * */
static int judge_initial_value(struct progress_bar *progress_data)
{
    if (!progress_data) {
        printf("PARAM ERROR!\n");
        system("stty echo");
        system("clear");
        exit(0);
    }

    if (progress_data->speed <= 0 || progress_data->speed >= 1000000) {
        printf("Speed Initialization Error\n");
        return 0;
    } else if (progress_data->outside_color < 40 ||
             progress_data->outside_color > 47) {
        printf("outside_color Initialization Error\n");
        return 0;
    } else if (progress_data->inside_color < 40 ||
             progress_data->inside_color > 47) {
        printf("inside_color Initialization Error\n");
        return 0;
    } else if (progress_data->length < 0 ||
             progress_data->length > 100) {
        printf("length Initialization Error\n");
        return 0;
    } else if ((progress_data->x_direction < 0 ||
             progress_data->x_direction > 20) ||
             (progress_data->y_direction < 0 ||
              progress_data->y_direction > 20)) {
        printf("direction Initialization Error\n");
        return 0;
    }
    return 1;
}

/* 代替printf("\n")*/
static void instead_of_enter(void)
{
    MOVEDOWN(1);
    printf("\r");
}

/* 打印进度条外框 */
static void draw_outside(int progress_outside_color, int length, int x_direction, int y_direction)
{
    int count;

    MOVEDOWN(y_direction);
    MOVERIGHT(x_direction);

    /* 打印上边框 */
    for (count = 0; count < length; count++) {
        printf("\033[%dm", progress_outside_color);
        printf(" ");
    }
    printf(REDUCTION);
    instead_of_enter();

    /* 打印左边框 */
    MOVERIGHT(x_direction);
    printf("\033[%dm", progress_outside_color);
    printf(" ");
    printf(REDUCTION);
    instead_of_enter();

    /* 打印下边框 */
    MOVERIGHT(x_direction);
    for (count = 0; count < length; count++) {
        printf("\033[%dm", progress_outside_color);
        printf(" ");
    }
    printf(REDUCTION);
    instead_of_enter();

    /* 打印右边框 */
    MOVERIGHT(length-1);
    MOVEUP(2);
    MOVERIGHT(x_direction);
    printf("\033[%dm", progress_outside_color);
    printf(" ");
    printf(REDUCTION);

    instead_of_enter();
    instead_of_enter();
    MOVEUP(2);
}

/* 打印进度条和百分比*/
static void draw_inside(int color, int length, int speed, int x_direction, int y_direction)
{
    HIDE_CURSOR();
    int count, multiple, times, printing_times;
    float quotient;
    int progress_length = 0;
    quotient = 100.0/(float)length;
    multiple = 1;
    times = 1;

    for (count = 0; 1; count++) {
        MOVERIGHT(1);
        if (count >= quotient * multiple) {
            MOVERIGHT(x_direction);
            /* 打印进度条 */
            for (printing_times = times; printing_times > 0; printing_times--) {
                printf("\033[%dm", color);
                printf(" ");
                printf(REDUCTION);
            }
            multiple++;
            times++;
            progress_length++;
            MOVERIGHT(length+3-progress_length);
        } else {
            MOVERIGHT(x_direction);
            MOVERIGHT(length+3);
        }

        /* 打印百分比 */
        printf("%d%%", count);
        if (count == 100) {
            printf("[COMPLETED]");
            break;
        }

        printf("\r");
        fflush(stdout);
        usleep(speed);
    }
    SHOW_CURSOR();
    printf("\n");
    printf("\n");
    printf("\n");
}